﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DDD.Core {
    /// <summary>
    /// コンポジット モード クラス
    /// </summary>
    /// <remarks>
    /// コンポジット モードはアピアランスの構成要素の１つで、シェーダーのフラグメント出力を
    /// フレーム バッファーに書き込む際の処理をカプセル化します。
    /// <para>
    /// <h3>デプス テスト</h3>
    /// デプス テストは、フラグメントのデプスとデプス バッファーの値を比較してフラグメントを破棄する処理です。
    /// デプス テストが無効の場合は重なり合う物体を正しくレンダリングするために遠くの物体から順番にシェーダーに送る必要がありますが、
    /// このテストが有効な場合はすでに描画された物体より奥の物体は棄却されるので順番に依存せず描画する事が可能です。
    /// デプス テストの有効無効は <see cref="SetDepthTestEnable()"/> で変更可能です。
    /// 比較に使用されるデプス関数は <see cref="SetDepthComparisonFunc()"/> で、
    /// <see cref="DepthComparisonFunction"/> の中から変更可能です。
    /// 選択できるデプス関数の詳細については、<see cref="DepthComparisonFunction"/> の解説を参照してください。
    /// デフォルトは <see cref="DepthComparisonFunction.LEqual"/> です。
    /// </para>
    /// <para>
    /// <h3>カラー ブレンド</h3>
    /// デプス テストを通過したフラグメントは既存のカラー バッファーの値と合成（ブレンド）されて、バッファーに書き込まれます。
    /// カラー ブレンド関数はカラー バッファーの値とフラグメントの値の合成方法を定義します。
    /// 詳しくは <see cref="ColorBlendFunction"/> の解説を参照してください。
    /// デフォルトは <see cref="ColorBlendFunction.Replace"/> でフラグメントの値でカラー バッファーを置き換えます。
    /// 半透明物体の描画はカラー ブレンド関数を <see cref="ColorBlendFunction.Alpha"/> に設定して、
    /// 物体を奥から順番に描画する必要があります。
    /// </para>
    /// <para>
    /// <h3>マスク</h3>
    /// カラー バッファーおよびデプス バッファーへの書き込みは、追加で特定のチャネルのみを選択的にマスクする事が可能です。
    /// 書き込みマスクが設定されたチャンネルへは一切書き込まれません。
    /// </para>
    /// </remarks>
    /// <para>
    /// <h3>その他のテスト</h3>
    /// αテストはシェーダーの中でユーザー自身が <c>discard</c> キーワードを使って実装します。
    /// 固定機能としてのαテストはありません。
    /// </para>
    /// <para>
    /// <h3>マルチ レンダー ターゲットとコンポジット モード</h3>
    /// シェーダーは同時に複数のフラグメントを出力し、別々のカラー バッファーに書き込む事が可能です。
    /// これをマルチ レンダー ターゲットと呼びます。
    /// カラー ブレンド関数とカラーマスクはすべてのカラー バッファーで共通して使用します。
    /// 個別に指定する事はできません。
    /// カラー バッファーとマルチ レンダー ターゲットの詳細については、<see cref="FrameBuffer"/> クラスの解説を参照してください。
    /// <note type="implement">
    /// これは <see cref="OpenGL"/> ではなく DDD の仕様。
    /// この仕様は正しいか？
    /// </note>
    /// </para>
    /// <seealso cref="Appearance"/>
    /// <seealso cref="DepthComparisonFunction"/>
    /// <seealso cref="ColorBlendFunction"/>
    /// <seealso cref="FrameBuffer"/>
    public class CompositeMode : Object {
        #region Field
        uint colorWriteMask;
        uint depthWriteMask;
        bool depthTestEnabled;
        DepthComparisonFunction depthComparisonFunction;
        ColorBlendFunction colorBlendFunction;
        #endregion

        #region Constructor
        /// <summary>
        /// コンストラクター
        /// </summary>
        /// <remarks>
        /// 初期値は以下のとおりです。
        /// <list type="bullete">
        ///   <item><see cref="ColorWriteMask"/> = 0xffffffff</item>
        ///   <item><see cref="DepthWriteMask"/> = 0xffffffff</item>
        ///   <item><see cref="IsDepthTestEnabled"/> = <c>true</c></item>
        ///   <item><see cref="DepthComparisonFunction"/></item>
        ///   <item><see cref="ColorBlendFunction"/> = <see cref="DepthComparisonFunction.Replace"/></item>
        /// </list>
        /// </remarks>
        /// <seealso cref="DepthComparisonFunction"/>
        /// <seealso cref="ColorBlendFunction"/>
        public CompositeMode () {
            this.colorWriteMask = 0xffffffff;
            this.depthWriteMask = 0xffffffff;
            this.depthTestEnabled = true;
            this.depthComparisonFunction = DepthComparisonFunction.LessEqual;
            this.colorBlendFunction = ColorBlendFunction.Replace;
        }
        #endregion

        #region Property
        /// <summary>
        /// デプステストの有効無効フラグ
        /// </summary>
        /// <remarks>
        /// 現在デプス テストが有効か無効化のフラグです。
        /// </remarks>
        /// <seealso cref="DepthComparisonFunction"/>
        public bool IsDepthTestEnabled {
            get { return depthTestEnabled; }
        }

        /// <summary>
        /// デプステストで使用される比較関数
        /// </summary>
        /// <remarks>
        /// デプス テストはこの比較関数を使用し、通過するフラグメントを決定します。
        /// </remarks>
        public DepthComparisonFunction DepthComparisonFunction {
            get { return depthComparisonFunction; }
        }

        /// <summary>
        /// デプスバッファーへの書き込みマスク
        /// </summary>
        /// <remarks>
        /// マスクのビットが立っているチャンネルのみ書き込み可能です。
        /// 0xff（書き込み可） か 0x00 （書き込み不可）が返ります。
        /// </remarks>
        public uint DepthWriteMask {
            get { return depthWriteMask; }
        }



        /// <summary>
        /// カラー バッファーへのブレンド関数
        /// </summary>
        /// <remarks>
        /// フラグメントをカラー バッファーへ書き込む時のブレンド関数です。
        /// 複数のカラー バッファーが存在する時は、そのすべてに作用します。
        /// ブレンド関数を個別に指定する事はできません。
        /// </remarks>
        /// <seealso cref="ColorBlendFunction"/>
        public ColorBlendFunction ColorBlendFunction {
            get { return colorBlendFunction; }
        }

     
        /// <summary>
        /// カラー バッファーへの書き込みマスク
        /// </summary>
        /// <remarks>
        /// フラグメントをカラーバッファーへ書き込む時の書き込みマスクです。
        /// 複数のカラー バッファーが存在する場合は、そのすべてに作用します。
        /// 書き込みマスクを個別に指定する事はできません。
        /// チャンネル毎に 0xff（書き込み可） か 0x00（書き込み不可） で返ります。
        /// </remarks>
        public uint ColorWriteMask {
            get { return colorWriteMask; }
        }



        /// <inheritdoc/>
        public override IEnumerable<Object> References {
            get {
                return base.References;
            }
        }
        #endregion

        #region Method

        /// <summary>
        /// ブレンド関数の変更
        /// </summary>
        /// <remarks>
        /// カラー ブレンド関数を <seealso cref="ColorBlendFunction"/> の中から選択します。
        /// ブレンド関数はすべてのカラー バッファーで共通で使用されます。
        /// </remarks>
        /// <param name="func">ブレンド方式</param>
        public void SetColorBlendFunction (ColorBlendFunction func) {
            this.colorBlendFunction = func;
        }

    

        /// <summary>
        /// カラー書き込みマスクの変更
        /// </summary>
        /// <remarks>
        /// カラー書き込みマスクを変更します。
        /// マスクは各チャネル毎に1バイトで指定し、0x00 が書き込み無効で
        /// それ以外（0xffを推奨）の時が書き込み可能です｡
        /// 指定は <c>uint</c> 型の整数値で RGBA の順番で指定します｡
        /// 書き込みマスクはすべてのカラーバッファーで共通で使用されます｡
        /// </remarks>
        /// <param name="mask">書き込みマスク</param>
        public void SetColorMask (uint mask) {
            this.colorWriteMask = mask;
        }



        /// <summary>
        /// デプス書き込みマスクの変更
        /// </summary>
        /// <remarks>
        /// デプス書き込みマスクを変更します。
        /// デプス マスクは1チャネルのみで 0x00 か 0xFF で指定します。
        /// 0x00 の時書き込み禁止で、それ以外（0xFFを推奨）の時に書き込み可能です。
        /// </remarks>
        /// <param name="mask">書き込みマスク</param>
        public void SetDepthMask (uint mask) {
            this.depthWriteMask = mask;
        }

        /// <summary>
        /// デプス テストの有効無効の変更
        /// </summary>
        /// <remarks>
        /// デプス テストの有効無効を変更します。
        /// デプス テストを無効にすると同時にデプスバッファーへの書き込みも無効化されます（注釈を参照）。
        /// <note type="important">
        /// これは OpenGL の仕様です。デプス テストを常に通過して、なおかつデプス バッファーも更新したい場合は、
        /// デプス テストは有効のまま、デプス関数として <see cref="DepthComparisonFuncion.Always"/> を選択してください。
        /// 詳細については OpenGL の仕様書を参照してください。
        /// </note>
        /// </remarks>
        /// <param name="enable">有効無効</param>
        /// <seealso cref="SetDepthComparisonFunction"/>
        public void SetDepthTestEnable (bool enable) {
            this.depthTestEnabled = enable;
        }

        /// <summary>
        /// デプス比較関数の変更
        /// </summary>
        /// <remarks>
        /// デプス比較関数を <see cref="DepthComparisonFunction"/> の中から選択します。
        /// </remarks>
        /// <param name="func">デプス比較関数"</param>
        /// <seealso cref="DepthComparisonFunction"/>
        public void SetDepthComparisonFunction (DepthComparisonFunction func) {
            this.depthComparisonFunction = func;
        }

        /// <inheritdoc/>
        public override string ToString () {
            return string.Format ("CompositeMode: DepthTest={0}, DepthComparison={1}, DepthWriteMask={2}, ColorBlend={3}, ColorWriteMask={4}",
                IsDepthTestEnabled, DepthComparisonFunction, DepthWriteMask, ColorBlendFunction, ColorWriteMask);
        }

        #endregion
    }
}
